package events;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

import eduni.simjava.Sim_entity;
import eduni.simjava.Sim_event;
import eduni.simjava.Sim_port;
import eduni.simjava.Sim_stat;
import eduni.simjava.Sim_system;

public class CommonServer extends Sim_entity implements Server {

	//Ports to communicate with LAN and pool
	private Sim_port in;
	private Sim_port outToLan;

	//Server capacity
	private double bandwidth;
	
	private Sim_stat stat;
	
	//Boolean indicating if server is busy with a client
	private boolean isBusy;
	private double nextFreeTime;
	
	//Files and blocks of each one that are available 
	private HashMap<String, Set<Integer>> availableFiles;

	public CommonServer(String name, double bandwidth, HashMap<String, Set<Integer>> availableFiles) {
		super(name);
		this.availableFiles = availableFiles;
		
		in = new Sim_port("in");
		add_port(in);
		
		outToLan = new Sim_port("outToLan");
		add_port(outToLan);

		this.bandwidth = bandwidth;

		stat = new Sim_stat();
        stat.add_measure(Sim_stat.THROUGHPUT);
        stat.add_measure(Sim_stat.ARRIVAL_RATE);
        stat.add_measure(Sim_stat.QUEUE_LENGTH);
        stat.add_measure(Sim_stat.SERVICE_TIME);
        stat.add_measure(Sim_stat.UTILISATION);
        stat.add_measure(Sim_stat.WAITING_TIME);
        set_stat(stat);
        
        isBusy = false;
        nextFreeTime = 0;
	}

	@Override
	public void body() {

		while (Sim_system.running()) {

			Sim_event e = new Sim_event();
			// Get the next event
			sim_get_next(e);

			if (e.get_tag() == EventType.REQUEST_FILE) {
				
				isBusy = true;
				
				Client client = (Client) e.get_data();
				long amountBeingRequested = client.getAmountToDownload();
				double processingTime = amountBeingRequested / this.bandwidth;
				
				nextFreeTime = Sim_system.clock() + processingTime;
				
//				System.out.println("TIME: "+Sim_system.clock()+" SERVER "+this.get_name()+" ANSWER FOR "+client.get_name()+" FILE "+client.getFileBeingDownloaded()+" "+client.getBlockBeingRequested());
				
				// Process the event
				sim_process(processingTime);
				
				// The event has completed service
				sim_completed(e);
				
				isBusy = false;
				//Scheduling the file transfer to LAN
				sim_schedule(this.outToLan, 0.0, EventType.REQUEST_FILE, client);
			}
		}
	}
	
	public double getNextFreeTime(){
		return nextFreeTime;
	}

	public String getServerName() {
		return this.get_name();
	}
	
	@Override
	public boolean isBusy() {
		return isBusy;
	}
	
	/**
	 * This method will find if the current server has the requested block
	 * of a certain file
	 */
	@Override
	public boolean hasFileAvailable(String fileName, int blockRequested) {
		Set<Integer> fileData = availableFiles.get(fileName);
		if(fileData == null){
			return false;
		}
		
		if (fileData.contains(blockRequested)){
			return true;
		}
		
		return false;
	}

	public double getCapacity() {
		return bandwidth;
	}
	
	/**
	 * This method will update the information of files and blocks that are
	 * available 
	 * @param fileBeingRequested
	 * @param blockBeingRequested
	 */
	public void updateFileAvailableData(String fileBeingRequested, int blockBeingRequested) {
		Set<Integer> fileData = availableFiles.get(fileBeingRequested);
		if(fileData == null){
			fileData = new HashSet<Integer>();
			availableFiles.put(fileBeingRequested, fileData);
		}
		
		fileData.add(blockBeingRequested);
	}
}
